/*
 * Copyright (C) 2012  Romain DUBOIS
 * 
 * This file is part of NeMoS - Network and Modular Software
 * 
 * NeMoS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * NeMoS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with NeMoS.  If not, see <http://www.gnu.org/licenses/>.
 */
package nemos.launch.internal;

import java.util.HashSet;
import java.util.Set;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.service.log.LogListener;
import org.osgi.service.startlevel.StartLevel;

/**
 * LaunchActivator : The Launch bundle activator.
 * 
 * @author Romain DUBOIS
 */
public class LaunchActivator implements BundleActivator, FrameworkListener, BundleListener {

    /** The bundle context */
    private BundleContext mContext;
    /** The listener->display converter */
    private Converter mConverter;
    /** The launch logger */
    private LaunchLogger mLogger;
    /** The recorder */
    private Recorder mRecorder;
    /** The started bundle IDs */
    private final Set<Long> mStartedBundles;

    /**
     * Constructor
     */
    public LaunchActivator() {
        super();

        mStartedBundles = new HashSet<Long>();
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
     */
    @Override
    public synchronized void bundleChanged(final BundleEvent pEvent) {
        if (pEvent.getType() == BundleEvent.STARTED && mRecorder != null) {
            mRecorder.bundleStarted(pEvent.getBundle());
            mStartedBundles.add(Long.valueOf(pEvent.getBundle().getBundleId()));
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.osgi.framework.FrameworkListener#frameworkEvent(org.osgi.framework.FrameworkEvent)
     */
    @Override
    public synchronized void frameworkEvent(final FrameworkEvent pEvent) {
        // Not registered any more
        if (mRecorder == null) {
            return;
        }

        // Only those events are interesting
        if (pEvent.getType() == FrameworkEvent.STARTED
                || pEvent.getType() == FrameworkEvent.STARTLEVEL_CHANGED) {

            // Collect service
            final StartLevel lStartLevel = mRecorder.getStartLevel();
            if (lStartLevel != null) {
                final int lLevel = lStartLevel.getStartLevel();

                // Level reached : unregister
                if (mRecorder.update()) {
                    stop(mContext);
                }
                // Change the level
                else {
                    lStartLevel.setStartLevel(lLevel + 1);

                    // For first launch : always wait for fileinstall
                    if (mRecorder.isFirstLaunch()) {
                        try {
                            // As lock is kept, no other call can be performed
                            // So level is not increased too fast
                            Thread.sleep(mRecorder.getDefaultStepWaitingTime());
                        } catch (final InterruptedException e) {
                            Thread.currentThread().interrupt();
                            stop(mContext);
                        }
                    }
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
     */
    @Override
    public synchronized void start(final BundleContext pContext) {
        if (mRecorder == null) {
            mContext = pContext;

            mLogger = new LaunchLogger(mContext);
            mContext.registerService(LogListener.class.getName(), mLogger, null);

            mRecorder = new Recorder(mContext);
            mRecorder.init();

            mContext.addFrameworkListener(this);
            mContext.addBundleListener(this);

            mConverter = new Converter(mContext);

            mStartedBundles.clear();

            // Init started bundles
            for (final Bundle lBundle : mContext.getBundles()) {
                if (lBundle.getState() == Bundle.ACTIVE) {
                    mRecorder.bundleStarted(lBundle);
                    mStartedBundles.add(Long.valueOf(lBundle.getBundleId()));
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
     */
    @Override
    public synchronized void stop(final BundleContext pContext) {
        if (mRecorder != null) {
            mConverter.ended();

            pContext.removeBundleListener(this);
            pContext.removeFrameworkListener(this);

            mRecorder.stop();
            mRecorder = null;

            mLogger.stop();
            mLogger = null;

            mContext = null;
        }
    }
}
